package com.objecteffects.template.web.action;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.inject.Inject;

import com.objecteffects.template.model.UserEntity;
import com.objecteffects.template.oauth.Tumblr;
import com.objecteffects.template.service.UserEntityService;
import com.objecteffects.template.tumblr.TumblrResponse;
import com.objecteffects.template.tumblr.method.Blog;
import com.objecteffects.template.tumblr.method.UserInfo;
import com.objecteffects.template.util.logger.InjectLogger;
import com.objecteffects.template.web.utils.LoginRequired;
import com.objecteffects.template.web.utils.TumblrAccessToken;

import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.TumblrApi;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;

import org.slf4j.Logger;

import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.SimpleMessage;

/**
 * @author Rusty Wright
 */
@LoginRequired
public class TumblrCallbackActionBean extends BaseActionBean {
    @InjectLogger
    private Logger log;

    private static final String METHOD_USER_INFO = "user/info";

    private final TumblrAccessToken tumblrAccessToken;
    private final UserEntityService userEntityService;

    @Inject
    TumblrCallbackActionBean(final TumblrAccessToken _tumblrAccessToken,
        final UserEntityService _userEntityService) {
        this.tumblrAccessToken = _tumblrAccessToken;
        this.userEntityService = _userEntityService;
    }

    @DefaultHandler
    public Resolution startHandler() throws IOException {
        final String tumblrUrl = this.getContext().getTumblrUrl();

        if (tumblrUrl == null) {
            this.log.debug("tumblrUrl is null");

            this.getContext().getMessages()
                .add(new SimpleMessage("Tumblr url is null."));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        // this.debugUrlParameters();

        final Token requestToken = this.getContext().getRequestToken();

        if (requestToken == null) {
            this.log.warn("requestToken is null");

            this.getContext().getMessages()
                .add(new SimpleMessage("Request token is null."));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        this.log.debug("context requestToken: {}",
                       requestToken.getRawResponse());

        final String paramVerifier =
            this.getContext().getRequest().getParameter(Tumblr.OAUTH_VERIFIER);

        if (paramVerifier == null) {
            this.log.debug("paramVerifier is null");

            this.getContext().getMessages()
                .add(new SimpleMessage("Permission denied."));

            return new ForwardResolution(TemplateView.ERROR);
        }

        // Step 4
        final Verifier verifier = new Verifier(paramVerifier);

        this.log.debug("verifier: {}", verifier.getValue());

        final OAuthService service =
            new ServiceBuilder().provider(TumblrApi.class)
                .apiKey(Tumblr.CONSUMER_KEY).apiSecret(Tumblr.CONSUMER_SECRET)
                .build();

        // the requestToken you had from step 2
        /* token credentials; scribe Token includes token and secret */
        final Token accessToken =
            service.getAccessToken(requestToken, verifier);

        this.log.debug("accessToken: {}", accessToken.getRawResponse());

        final List<String> blogs = this.fetchBlogs(tumblrUrl, accessToken);

        if (blogs == null) {
            return new ForwardResolution(TemplateView.ERROR);
        }

        this.log.debug("blogs: {}", blogs);

        // final int dot = tumblrUrl.indexOf(".");
        // final String shortName = tumblrUrl.substring(0, dot);

        if (!blogs.contains(tumblrUrl)) {
            this.log.debug("blogs doesn't contain: {}", tumblrUrl);

            final StringBuffer msg = new StringBuffer();
            msg.append("You are not logged in on tumblr for that blog.");
            msg.append("<br />");
            msg.append("On tumblr logout from your current blog and log into the blog you want to manage.");
            msg.append("<br />");
            msg.append("You are logged in on tumblr as ");
            msg.append(blogs.get(0));
            msg.append(".");

            this.getContext().getMessages()
                .add(new SimpleMessage(msg.toString()));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        final UserEntity userEntity =
            this.userEntityService.findById(this.getContext().getUserId());

        if (!blogs.isEmpty()) {
            /*
             * using the first one; probably it's the primary one.
             */
            userEntity.setTumblrName(blogs.get(0));
        }
        else {
            userEntity.setTumblrName(null);
        }

        this.userEntityService.update(userEntity);

        this.tumblrAccessToken.saveToken(tumblrUrl, accessToken, this
            .getContext().getUserId());

        return new RedirectResolution(TumblrResponseUrlActionBean.class);
    }

    /**
     * @param tumblrUrl
     * @param accessToken
     * @return
     * @throws IOException
     */
    private List<String> fetchBlogs(final String tumblrUrl,
                                    final Token accessToken) throws IOException {
        final TumblrResponse<UserInfo> built =
            new TumblrResponse.Builder<UserInfo>().accessToken(accessToken)
            .blog(tumblrUrl)
                .method(TumblrCallbackActionBean.METHOD_USER_INFO).setUser()
            .type(UserInfo.class).build();

        final UserInfo info = built.responseMapper();

        if (info == null) {
            this.log.debug("user info response is null");

            this.getContext()
                .getMessages()
            .add(new SimpleMessage(
                         "User info response is null; please try again"));

            return null;
        }

        final List<Blog> blogs = info.getResponse().getUser().getBlogs();

        if (blogs.isEmpty()) {
            this.log.debug("user info blogs is empty");

            this.getContext().getMessages()
            .add(new SimpleMessage("User info is empty; please try again"));

            return null;
        }

        final List<String> blogNames = new ArrayList<>();

        for (final Blog blog : blogs) {
            blogNames.add(blog.getName());
        }

        return blogNames;
    }

    /**
     */
    @SuppressWarnings("unused")
    private void debugUrlParameters() {
        @SuppressWarnings("unchecked")
        final Map<String, Object> map =
            this.getContext().getRequest().getParameterMap();

        for (final Map.Entry<String, Object> entry : map.entrySet()) {
            this.log.debug("key: {}, value: {}", entry.getKey(),
                           entry.getValue());
        }
    }
}
